#include "UILoader.hpp"
#include "BarChart.hpp"
#include "MultiLineChart.hpp"
#include "glibmm/dispatcher.h"
#include "glibmm/error.h"
#include "glibmm/fileutils.h"
#include "glibmm/markup.h"
#include "glibmm/refptr.h"
#include "gtkmm/builder.h"
#include "gtkmm/error.h"
#include "gtkmm/widget.h"
#include "sigc++/functors/mem_fun.h"
#include <string>
#include <thread>
#include <spdlog/spdlog.h>
#include <filesystem>
#include <stdexcept>
#include <sstream>

template<class T>
T* UILoader::GetInstance(const char* name)
{
  T* ptr=refBuilder->get_widget<T>(name);
  if(ptr==nullptr)
    throw std::runtime_error(fmt::format("无法获取{}实例",name));
  ptr->set_name(name);
  return ptr;
}

//类型萃取
template<class T> T* UILoader::GetInstancePlus(T* type,const char* name)
{
  return GetInstance<T>(name);
}

UILoader::UILoader()
  : chart1(180, 0, 10, 1)
  , chart2(180, 0, 10, 1)
  , chart3(180, 0, 10)
{
  if(!std::filesystem::exists("showresult.ui"))
    throw std::runtime_error("UI resource file is finished");
}

void UILoader::signal_activate_app()
{
  refBuilder=Gtk::Builder::create();
  try {
    refBuilder->add_from_file("showresult.ui");
  }
  catch(const Glib::FileError& ex)
  {
    spdlog::error("UILoader: {}",ex.what());
    throw;
  }
  catch(const Glib::MarkupError& ex)
  {
    spdlog::error("UILoader: {}",ex.what());
    throw;
  }
  catch(const Gtk::BuilderError& ex)
  {
    spdlog::error("UILoader: {}",ex.what());
    throw;
  }

#define XMACRO_STR(val) #val
#define GET_INSTANCE_AUTO(name) \
  name=GetInstancePlus<>(name,XMACRO_STR(name))

  GET_INSTANCE_AUTO(mainWindow);
  GET_INSTANCE_AUTO(btnChooseOriJoint);
  GET_INSTANCE_AUTO(btnChooseStdVideo);
  GET_INSTANCE_AUTO(btnChooseStdJoint);
  GET_INSTANCE_AUTO(btnChooseOriVideo);
  GET_INSTANCE_AUTO(btnChooseDiffJoint);
  GET_INSTANCE_AUTO(listRecord);
  GET_INSTANCE_AUTO(videoOri);
  GET_INSTANCE_AUTO(videoStd);
  GET_INSTANCE_AUTO(glChart1);
  GET_INSTANCE_AUTO(glChart2);
  GET_INSTANCE_AUTO(glChart3);
  GET_INSTANCE_AUTO(swiControlContinue);
  GET_INSTANCE_AUTO(btnControlStart);
  GET_INSTANCE_AUTO(btnControlStop);
  GET_INSTANCE_AUTO(btnRefreshList);

#undef XMACRO_STR
#undef GET_INSTANCE_AUTO

#define AUTO_CREATE_BUTTON_SIGNAL(obj) obj->signal_clicked().connect(sigc::mem_fun(*this,&UILoader::signal_click_##obj));
  AUTO_CREATE_BUTTON_SIGNAL(btnChooseOriVideo)
  AUTO_CREATE_BUTTON_SIGNAL(btnChooseStdVideo)
  AUTO_CREATE_BUTTON_SIGNAL(btnChooseOriJoint)
  AUTO_CREATE_BUTTON_SIGNAL(btnChooseStdJoint)
  AUTO_CREATE_BUTTON_SIGNAL(btnChooseDiffJoint)
  AUTO_CREATE_BUTTON_SIGNAL(btnControlStart)
  AUTO_CREATE_BUTTON_SIGNAL(btnControlStop)
#undef AUTO_CREATE_BUTTON_SIGNAL

  m_refTreeModel=Gtk::TreeStore::create(m_columns);
  listRecord->set_model(m_refTreeModel);
  listRecord->set_reorderable();

  listRecord->append_column("名称",m_columns.m_col_name);
  listRecord->append_column("时间",m_columns.m_col_time);
  listRecord->signal_row_activated().connect(sigc::mem_fun(*this,&UILoader::signal_activate_listRecord));
  swiControlContinue->signal_state_set().connect(sigc::mem_fun(*this,&UILoader::signal_set_swiControlContinue),false);

  videoOri->set_draw_func(sigc::mem_fun(*this,&UILoader::draw_oriImage));
  videoStd->set_draw_func(sigc::mem_fun(*this,&UILoader::draw_stdImage));

  glChart1->set_draw_func(sigc::mem_fun(*this,&UILoader::signal_render_glChart1));
  glChart2->set_draw_func(sigc::mem_fun(*this,&UILoader::signal_render_glChart2));
  glChart3->set_draw_func(sigc::mem_fun(*this,&UILoader::signal_render_glChart3));

  patch_refresh.reset(new Glib::Dispatcher);
  patch_refresh->connect(sigc::mem_fun(*this,&UILoader::signal_refresh));

  app->add_window(*mainWindow);
  mainWindow->set_visible(true);
}

void UILoader::Boot()

{
  workThread=std::thread([this](){
    app=Gtk::Application::create("org.hbty.ShowResult");
    app->signal_activate().connect(sigc::mem_fun(*this,&UILoader::signal_activate_app));
    app->run();
    funcUIExit();
  });
}

UILoader::~UILoader()
{
  if(workThread.joinable())
    workThread.join();
}


void UILoader::signal_render_glChart1(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height)
{
  chart1.DrawCall(cr,width,height,dynamic_cast<Gtk::Widget*>(glChart1));
}

void UILoader::signal_render_glChart2(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height)
{
  chart2.DrawCall(cr,width,height,dynamic_cast<Gtk::Widget*>(glChart2));

}

void UILoader::signal_render_glChart3(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height)
{
  chart3.DrawCall(cr,width,height,dynamic_cast<Gtk::Widget*>(glChart3));
}

#define AUTO_CHOOSE_FILE(func,typeval,typemine) \
  auto dialog=Gtk::FileDialog::create();\
  auto filters=Gio::ListStore<Gtk::FileFilter>::create();\
  auto filter_text=Gtk::FileFilter::create();\
  filter_text->set_name(typeval);\
  filter_text->add_mime_type(typemine);\
  filters->append(filter_text);\
  auto filter_any=Gtk::FileFilter::create();\
  filter_any->set_name("Any files");\
  filter_any->add_pattern("*");\
  filters->append(filter_any);\
  dialog->set_filters(filters);\
  dialog->open(\
    [this,dialog](const Glib::RefPtr<Gio::AsyncResult>& result){\
      try{\
        auto file=dialog->open_finish(result);\
        auto filename=file->get_path();\
        func(filename);\
      }\
      catch (const Gtk::DialogError& err)\
      {\
        return;\
      }\
      catch(const Glib::Error& err)\
      {\
        return;\
      }\
  });

void UILoader::signal_click_btnChooseStdVideo()
{
  AUTO_CHOOSE_FILE(funcSetStdVideo, "mkv", "video/matroska");
}

void UILoader::signal_click_btnChooseOriJoint()
{
  AUTO_CHOOSE_FILE(funcSetOriJoint, "text", "text/plain");
}

void UILoader::signal_click_btnChooseStdJoint()
{
  AUTO_CHOOSE_FILE(funcSetStdJoint, "text", "text/plain");
}

void UILoader::signal_click_btnChooseOriVideo()
{
  AUTO_CHOOSE_FILE(funcSetOriVideo, "mkv", "video/matroska");
}

void UILoader::signal_click_btnChooseDiffJoint()
{
  AUTO_CHOOSE_FILE(funcSetDiffJoint, "text", "text/plain");
}

#undef AUTO_CHOOSE_FILE

void UILoader::signal_click_btnControlStart()
{
  funcSetPlay();
}

void UILoader::signal_click_btnControlStop()
{
  funcSetStop();
}

bool UILoader::signal_set_swiControlContinue(bool status)
{
  funcSetContinue(status);
  return true;
}

void UILoader::SetOriImage(int width,int height,unsigned char* img)
{
  std::lock_guard<std::mutex> lck(oriImageMut);
  //转rgb
  oriImage=Gdk::Pixbuf::create_from_data
    (img,Gdk::Colorspace::RGB,false,8,width,height,width*3,
     [](const guint8* data){delete [] data;});
  patch_refresh->emit();
}

void UILoader::SetStdImage(int width,int height,unsigned char* img)
{
  std::lock_guard<std::mutex> lck(stdImageMut);
  //转rgb
  stdImage=Gdk::Pixbuf::create_from_data
    (img,Gdk::Colorspace::RGB,false,8,width,height,width*3,
     [](const guint8* data){delete [] data;});
  patch_refresh->emit();
}

void UILoader::draw_stdImage(const Cairo::RefPtr<Cairo::Context>& cr,int width,int height)
{
  std::lock_guard<std::mutex> lck(stdImageMut);
  if(!stdImage)
  {
    cr->set_source_rgb(1,1,1);
    cr->rectangle(0,0,width,height);
    cr->fill();
    cr->paint();
  }
  else
  {
    double scalePara=(double)height/stdImage->get_height();

    long coordY=scalePara*stdImage->get_width();
    long coordOffset=(float)width/2-(float)coordY/2;
    Gdk::Cairo::set_source_pixbuf(
      cr, 
      stdImage->scale_simple(
        coordY,
        height,
        Gdk::InterpType::NEAREST),
      coordOffset,
      0);
    cr->paint();
  }
}

void UILoader::draw_oriImage(const Cairo::RefPtr<Cairo::Context>& cr,int width,int height)
{
  std::lock_guard<std::mutex> lck(oriImageMut);
  if(!oriImage)
  {
    cr->set_source_rgb(1,1,1);
    cr->rectangle(0,0,width,height);
    cr->fill();
    cr->paint();
  }
  else
  {
    double scalePara=(double)height/oriImage->get_height();

    long coordY=scalePara*oriImage->get_width();
    long coordOffset=(float)width/2-(float)coordY/2;
    Gdk::Cairo::set_source_pixbuf(
      cr, 
      oriImage->scale_simple(
        coordY,
        height,
        Gdk::InterpType::NEAREST),

      coordOffset,
      0);
    cr->paint();

  }
}

void UILoader::signal_refresh()
{
  videoOri->queue_draw();
  videoStd->queue_draw();
  glChart1->queue_draw();
  glChart2->queue_draw();
  glChart3->queue_draw();
}

void UILoader::InputData(float chart1_,float chart2_,float chart3_)
{
  chart1.InputData(chart1_,0);
  chart2.InputData(chart2_,0);
  chart3.InputData(chart3_);
}

void UILoader::signal_activate_listRecord(const Gtk::TreeModel::Path& path,Gtk::TreeViewColumn* column)
{
  const auto iter=m_refTreeModel->get_iter(path);
  if(iter)
  {
    const auto row=*iter;
    switch(row[m_columns.level])
    {
      case 1:
      break;
      case 2:
        std::stringstream ss;
        ss<<row[m_columns.path]<<R"(/)"<<"C1527A0090416.mkv";
        funcSetOriVideo(ss.str());
        ss.clear();
        ss<<row[m_columns.path]<<R"(/)"<<"C1527A0090416.frame";
        funcSetOriJoint(ss.str());
        ss.clear();
        ss<<row[m_columns.path]<<R"(/)"<<"output_3d.txt";
        funcSetDiffJoint(ss.str());
        ss.clear();
      break;
    };
  }
}

void UILoader::siganl_click_btnRefreshList()
{
  m_refTreeModel->clear();
  std::vector<long> listID=funcGetTaskList();
  for(long i : listID)
  {
    auto taskinf=funcGetTaskInfo(i);
    auto row=*(m_refTreeModel->append());
    row[m_columns.m_col_name]=std::get<0>(taskinf);
    row[m_columns.m_col_time]=std::get<1>(taskinf);
    row[m_columns.level]=1;
    row[m_columns.path]="";
    auto recordid=funcSearchHandleFinishItem(i);
    for(long j : recordid)
    {
      auto recordinf=funcGetItemInfo(j);
      auto record=*(m_refTreeModel->append(row.children()));
      record[m_columns.level]=2;
      record[m_columns.path]=std::get<2>(recordinf);
      record[m_columns.m_col_name]=std::to_string(std::get<0>(recordinf));
      record[m_columns.m_col_time]=std::get<1>(recordinf);
    }
  }
}

